# PSL imports
import getpass
import logging
import optparse
import os
import random
import string
import sys
import tempfile
import urllib2

# pyytup imports
import clientlogin
import uploadrequest

def _GenerateMultipartBoundary():
  chars = string.letters + string.digits
  chars_out = []
  for i in range(64):
    chars_out.append(random.choice(chars))

  return 'pyytup_' + ''.join(chars_out)


def _GetOptionParser():
  parser = optparse.OptionParser()
  parser.add_option('-c', '--categories', type='string',
                    help='Video categories')
  parser.add_option('-d', '--description', type='string',
                    help='Video description')
  parser.add_option('-e', '--email', type='string',
                    help='YouTube account email address')
  parser.add_option('-k', '--dev_key', type='string',
                    help='YouTube GData API developer key')
  parser.add_option('-p', '--password', type='string',
                    help='YouTube account password.')
  parser.add_option('-t', '--title', type='string',
                    help='Video title')
  parser.add_option('-w', '--keywords', type='string',
                    help='Video keywords')
  parser.add_option('--playlist_id', type='string',
                    help='An optional playlist id')

  return parser


def _WriteUploadRequestToFile(entry, boundary, video_file, stream):
  boundary_line = '--%s\n' % boundary

  # Atom entry section
  stream.write(boundary_line)
  stream.write('Content-Type: application/atom+xml; charset=UTF-8\n\n')
  stream.write(entry)

  # Binary section
  stream.write(boundary_line)
  stream.write('Content-Type: video/mpg\n')
  stream.write('Content-Transfer-Encoding: binary\n\n')
  
  # Buffer the write
  while True:
    data = video_file.read(1024)
    if len(data) == 0:
      break
    stream.write(data)

  stream.write('\n')
  stream.write(boundary_line + '--')
  
  
def main():

  logging.basicConfig(format='pyytup.py: %(message)s', level=logging.INFO)

  options, args = _GetOptionParser().parse_args()

  # Flag checks.
  if None in [options.title, options.categories, options.description,
              options.keywords]:
    raise ('Title, categories, description, and keywords must be specified '
        'on the command line.')

  if options.email is None:
    raise 'Email must be specified with a flag.'

  if len(args) != 1:
    print 'Exactly one video file should be specified.'
    return 1

  path = args[0]
  video_file = open(path)

  # Figure out file size
  video_file.seek(0, 2) # 0, 2 means offset of 0 from the end
  size = video_file.tell()
  video_file.seek(0) # 0 means beginning

  logging.info('Uploading file %s', path)
  logging.info('Video is %s bytes long.', size)

  MAX_UPLOAD_SIZE = 2 * 1024 ** 3
  if size > MAX_UPLOAD_SIZE:
    logging.error('Cannot upload.  Maximum YouTube upload size is %s',
                  MAX_UPLOAD_SIZE)
    return 1

  passwd = options.password
  if not passwd:
    passwd = getpass.getpass()
  
  username, token = clientlogin.FetchClientLoginAuthUserAndToken(
    options.email, passwd)

  multipart_boundary = _GenerateMultipartBoundary()
  
  _, filename = os.path.split(path)

  # TODO: Add other metadata types
  entry = {
    'title': options.title,
    'categories': options.categories,
    'description': options.description,
    'keywords': options.keywords
  }

  if options.playlist_id:
    entry['playlist_id'] = options.playlist_id

  # Do the title length check locally.
  if len(entry['title']) > 60:
    logging.error('Failing. Maximum YouTube title length is 60')
    return 1

  atom_entry = uploadrequest.GetAtomEntry(entry)

  buf = tempfile.TemporaryFile()
  _WriteUploadRequestToFile(atom_entry, multipart_boundary, video_file, buf)
  buf.seek(0)

  # Using urllib2 for now, as it handles headers, content-length, etc.
  # TODO: Add the ability to stream and rate limit by writing right to the
  # socket.
  req = urllib2.Request(
    'http://uploads.gdata.youtube.com/feeds/api/users/default/uploads',
    buf.read())
  req.add_header('Authorization', 'GoogleLogin auth="%s"' % token)
  req.add_header('GData-Version', 2)
  req.add_header('X-GData-Key', 'key=' + options.dev_key)
  req.add_header('Slug', filename)
  req.add_header('Content-type', 
                 'multipart/related; boundary="%s"' % multipart_boundary)
  req.add_header('Connection', 'close')

  try:
    res = urllib2.urlopen(req)
    print res.read()
  except urllib2.HTTPError, ex:
    print ex
    print ex.read()
    return 1 # Make sure we indicate error to caller.

if __name__ == '__main__':
  sys.exit(main())
